#include "stdafx.h"
#include <cstdio>
#include <cstring>

#include "SceneParser.h"
#include "Geometry.h"
#include "Blob.h"
#include "Algeb.h"
#include "Material.h"

using namespace rt;

Scene * SceneParser::ParseFromFile(const char * filename)
{
	mFile = fopen(filename, "r");
	if(mFile == NULL)
	{
		return NULL;
	}

	fscanf(mFile, "%s", mBuffer);
	if(_strcmpi(mBuffer, "scene") != 0)
	{
		return NULL;
	}

	mScene = new Scene;

	while(1)
	{
		fscanf(mFile, "%s", mBuffer);

		if(!_strcmpi(mBuffer, "end"))
			break;

		if(!_strcmpi(mBuffer, "general"))
			ParseGeneral();
		else if(!_strcmpi(mBuffer, "light"))
			ParseLight();
		else if(!_strcmpi(mBuffer, "material"))
			ParseMaterial();
		else if(!_strcmpi(mBuffer, "sphere"))
			ParseSphere();
		else if(!_strcmpi(mBuffer, "plane"))
			ParsePlane();
		else if(!_strcmpi(mBuffer, "box"))
			ParseBox();
		else if(!_strcmpi(mBuffer, "metaballgroup"))
			ParseBlob();
		else if(!_strcmpi(mBuffer, "algeb"))
			ParseAlgeb();
		else if(!_strcmpi(mBuffer, SINGLE_LINE_COMMENT))
			fgets(mBuffer, PARSER_BUFFER_LENGTH, mFile);
		else if(!_strcmpi(mBuffer, MULTI_LINE_COMMENT_START))
			ParseMultilineComment();
		else
			break;
	}


	fclose(mFile);

	return mScene;
}

void SceneParser::ParseMultilineComment()
{
	while(1)
	{
		fscanf(mFile, "%s", mBuffer);
		if(!_strcmpi(mBuffer, MULTI_LINE_COMMENT_END))
			break;
	}
}

void SceneParser::ParseGeneral()
{
	while(1)
	{
		fscanf(mFile, "%s", mBuffer);

		if(!_strcmpi(mBuffer, "end"))
			break;

		if(!_strcmpi(mBuffer, "ambient"))
		{
			Color color;
			fscanf(mFile, "%f %f %f", &color.R, &color.G, &color.B);
			mScene->SetAmbientColor(color);
		}
		else if(!_strcmpi(mBuffer, "resolution"))
		{
			fscanf(mFile, "%d %d", &mParsedScreenWidth, &mParsedScreenHeight);
			mScene->GetEditableCamera().SetResolution(mParsedScreenWidth, mParsedScreenHeight);
		}
		else if(!_strcmpi(mBuffer, "show_progress"))
		{
			int showProgress;
			fscanf(mFile, "%d", &showProgress);
			mParsedShowProgress = showProgress > 0;
		}
		else if(!_strcmpi(mBuffer, SINGLE_LINE_COMMENT))
			fgets(mBuffer, PARSER_BUFFER_LENGTH, mFile);
		else if(!_strcmpi(mBuffer, MULTI_LINE_COMMENT_START))
			ParseMultilineComment();
		else
			break;
	}
}

void SceneParser::ParseLight()
{
	Light * light = mScene->CreateLight();
	while(1)
	{
		fscanf(mFile, "%s", mBuffer);

		if(!_strcmpi(mBuffer, "end"))
			break;

		if(!_strcmpi(mBuffer, "position"))
		{
			float x, y, z;
			fscanf(mFile, "%f %f %f", &x, &y, &z);
			light->Position = XMVectorSet(x, y, z, 0.0f);
		}
		else if(!_strcmpi(mBuffer, "diffuse"))
		{
			float r, g, b;
			fscanf(mFile, "%f %f %f", &r, &g, &b);
			light->Diffuse = XMVectorSet(r, g, b, 0.0f);
		}
		else if(!_strcmpi(mBuffer, "strength"))
		{
			float s;
			fscanf(mFile, "%f", &s);
			light->Strength = s;
		}
		else if(!_strcmpi(mBuffer, "radius"))
		{
			float r;
			fscanf(mFile, "%f", &r);
			light->Radius = r;
		}
		else if(!_strcmpi(mBuffer, "falloff"))
		{
			float f;
			fscanf(mFile, "%f", &f);
			light->Falloff = f;
		}
		//else if(!_strcmpi(_buffer, "specular"))
		//{
		//	float r, g, b;
		//	fscanf(_file, "%f %f %f", &r, &g, &b);
		//	light->_specular = Color(r, g, b);
		//	light->Specular = XMVectorSet(r, g, b, 0.0f);
		//}
		else if(mBuffer[0] != '#')
			break;
	}
}

void SceneParser::ParseMaterial()
{
	Material * material = mScene->CreateMaterial();
	while(1)
	{
		fscanf(mFile, "%s", mBuffer);

		if(!_strcmpi(mBuffer, "end"))
			break;

		if(!_strcmpi(mBuffer, "name"))
		{
			fscanf(mFile, "%s", mBuffer);
			mMaterialMap[mBuffer] = material;
		}
		else if(!_strcmpi(mBuffer, "diffuse"))
		{
			float r, g, b;
			fscanf(mFile, "%f %f %f", &r, &g, &b);
			material->Diffuse = XMVectorSet(r, g, b, 0.0f);
		}
		else if(!_strcmpi(mBuffer, "specular"))
		{
			float r, g, b;
			fscanf(mFile, "%f %f %f", &r, &g, &b);
			material->Specular = XMVectorSet(r, g, b, 0.0f);
		}
		else if(!_strcmpi(mBuffer, "specPower"))
		{
			fscanf(mFile, "%f", &material->SpecularPower);
		}
		else if(!_strcmpi(mBuffer, "reflectance"))
		{
			fscanf(mFile, "%f", &material->Reflectance);
		}
		else if(!_strcmpi(mBuffer, "transparency"))
		{
			fscanf(mFile, "%f", &material->Transparency);
		}
		else if(!_strcmpi(mBuffer, "proctex"))
		{
			fscanf(mFile, "%s", mBuffer);
			if(!_strcmpi(mBuffer, "none")) material->ProcTex = Material::NONE;
			else if(!_strcmpi(mBuffer, "noise")) material->ProcTex = Material::NOISE;
			else if(!_strcmpi(mBuffer, "marble")) material->ProcTex = Material::MARBLE;
		}
		else if(mBuffer[0] != '#')
			break;
	}
}

void SceneParser::ParseSphere()
{
	uint sphIdx = mScene->getNextSphereIndex();
	Sphere * sph = &mScene->Spheres[sphIdx];

	while(1)
	{
		fscanf(mFile, "%s", mBuffer);

		if(!_strcmpi(mBuffer, "end"))
			break;

		if(!_strcmpi(mBuffer, "position"))
		{
			float x, y, z;
			fscanf(mFile, "%f %f %f", &x, &y, &z);
			sph->SetCenter(XMVectorSet(x, y, z, 0.0f));
		}
		else if(!_strcmpi(mBuffer, "radius"))
		{
			float r;
			fscanf(mFile, "%f", &r);
			sph->SetRadiusSquared(r*r);
		}
		else if(!_strcmpi(mBuffer, "material"))
		{
			fscanf(mFile, "%s", mBuffer);
			mScene->SphereMats[sphIdx] = mMaterialMap[mBuffer];
		}
		else if(mBuffer[0] != '#')
			break;
	}

}

void SceneParser::ParsePlane()
{
	uint planeIdx = mScene->getNextPlaneIndex();
	Plane * plane = &mScene->Planes[planeIdx];

	float nx, ny, nz, px, py, pz;

	bool pointRead = false;
	bool normalRead = false;

	while(1)
	{
		fscanf(mFile, "%s", mBuffer);

		if(!_strcmpi(mBuffer, "end"))
			break;

		if(!_strcmpi(mBuffer, "point"))
		{
			fscanf(mFile, "%f %f %f", &px, &py, &pz);

			pointRead = true;
			if(normalRead)
				plane->SetPointAndNormal(XMVectorSet(px, py, pz, 0.0f), XMVectorSet(nx, ny, nz, 0.0f));
		}
		else if(!_strcmpi(mBuffer, "normal"))
		{
			fscanf(mFile, "%f %f %f", &nx, &ny, &nz);

			normalRead = true;
			if(pointRead)
				plane->SetPointAndNormal(XMVectorSet(px, py, pz, 0.0f), XMVectorSet(nx, ny, nz, 0.0f));
		}
		else if(!_strcmpi(mBuffer, "material"))
		{
			fscanf(mFile, "%s", mBuffer);
			mScene->PlaneMats[planeIdx] = mMaterialMap[mBuffer];
		}
		else if(mBuffer[0] != '#')
			break;
	}
}

void SceneParser::ParseBox()
{
	uint boxIdx = mScene->getNextBoxIndex();
	Box * box = &mScene->Boxes[boxIdx];

	float sx = 1, sy = 1, sz = 1, px = 0, py = 0, pz = 0;

	bool sizeRead = false;
	bool posRead = false;

	while(1)
	{
		fscanf(mFile, "%s", mBuffer);

		if(!_strcmpi(mBuffer, "end"))
			break;
		
		if(!_strcmpi(mBuffer, "size"))
		{
			fscanf(mFile, "%f %f %f", &sx, &sy, &sz);

			sizeRead = true;
			if(posRead)
				box->SetPositionAndSize(XMVectorSet(px, py, pz, 0.0f), XMVectorSet(sx, sy, sz, 0.0f));
		}
		else if(!_strcmpi(mBuffer, "position"))
		{
			fscanf(mFile, "%f %f %f", &px, &py, &pz);

			posRead = true;
			if(sizeRead)
				box->SetPositionAndSize(XMVectorSet(px, py, pz, 0.0f), XMVectorSet(sx, sy, sz, 0.0f));
		}
		else if(!_strcmpi(mBuffer, "material"))
		{
			fscanf(mFile, "%s", mBuffer);
			mScene->BoxMats[boxIdx] = mMaterialMap[mBuffer];
		}
		else if(mBuffer[0] != '#')
			break;
	}

	if( (!sizeRead && posRead) || (sizeRead && !posRead) )
		box->SetPositionAndSize(XMVectorSet(px, py, pz, 0.0f), XMVectorSet(sx, sy, sz, 0.0f));
}

void SceneParser::ParseBlob()
{
	uint blobIdx = mScene->getNextBlobIndex();
	Blob * blob = &mScene->Blobs[blobIdx];

	bool parsingBall = false;
	Metaball * currBall = NULL;

	while(1)
	{
		fscanf(mFile, "%s", mBuffer);

		if(!_strcmpi(mBuffer, "end"))
		{
			if(parsingBall)
			{
				currBall = NULL;
				parsingBall = false;
			}
			else
			{
				blob->UpdateRadii();
				break;
			}
		}
		else if(!_strcmpi(mBuffer, "metaball") && !parsingBall)
		{
			parsingBall = true;
			currBall = blob->CreateMetaball();
		}
		else if(!_strcmpi(mBuffer, "position") && parsingBall)
		{
			float x, y, z;
			fscanf(mFile, "%f %f %f", &x, &y, &z);
			currBall->Center = XMVectorSet(x, y, z, 0.0f);
		}
		else if(!_strcmpi(mBuffer, "radius") && parsingBall)
		{
			fscanf(mFile, "%f", &currBall->Radius);
		}
		else if(!_strcmpi(mBuffer, "material") && parsingBall)
		{
			fscanf(mFile, "%s", mBuffer);
			mScene->BlobMats[blobIdx] = mMaterialMap[mBuffer];
		}
		else if(mBuffer[0] != '#')
			break;
	}

}

void SceneParser::ParseAlgeb()
{
	uint algebIdx = mScene->getNextAlgebIndex();
	Algeb * algeb = &mScene->Algebs[algebIdx];

	while(1)
	{
		fscanf(mFile, "%s", mBuffer);

		if(!_strcmpi(mBuffer, "end"))
		{
			break;
		}
		else if(!_strcmpi(mBuffer, "material"))
		{
			fscanf(mFile, "%s", mBuffer);
			mScene->AlgebMats[algebIdx] = mMaterialMap[mBuffer];
		}
		else if(mBuffer[0] != '#')
			break;
	}

}

